Skip to content

Conversation

johnsimons
Copy link
Member

@johnsimons johnsimons commented Sep 15, 2025

We found an issue with the cleaning-up routines that can cause certain instances to be cleaned up before others.
We have started a discussion in vitest to figure out if the behaviour is a bug or if we are just using it incorrectly.

In the meantime, we have addressed the issue by refactoring the code to do the cleanup in a more consistent way.

This is to fix the flaky test failures that cause:
Error: ReferenceError: window is not defined
 ❯ stopTimer src/composables/autoRefresh.ts:12:7
 ❯ startTimer src/composables/autoRefresh.ts:20:5
 ❯ executeAndResetTimer src/composables/autoRefresh.ts:31:7
 ❯ Timeout._onTimeout src/composables/autoRefresh.ts:22:7
 ❯ listOnTimeout node:internal/timers:588:17
 ❯ processTimers node:internal/timers:523:7
Added run to vitest so by default we not running in watch mode
Otherwise the cleanup happens in a non deterministic order
@johnsimons johnsimons changed the title Replace window.setTimeout with just setTimeout Addressing issues with failing flaky tests Oct 7, 2025
@johnsimons johnsimons requested a review from PhilBastian October 7, 2025 05:37
"lint": "eslint .",
"preview": "vite preview",
"test:component": "vitest ./src",
"test:component": "vitest run ./src",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By default vitest runs in watch mode, but in our CI we always want it to perform a single run without watch mode.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was able to detect that it was running from CI and not hold the execution in an infinite watch. Making it explicit with run is good 👍

"@testing-library/vue": "8.1.0",
"@tsconfig/node18": "18.2.4",
"@types/bootstrap": "5.2.10",
"@types/jsdom": "27.0.0",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were missing this typescript type

Comment on lines -36 to -55
function deleteAllCookies() {
const cookies = document.cookie.split(";");

for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i];
const eqPos = cookie.indexOf("=");
const name = eqPos > -1 ? cookie.substr(0, eqPos) : cookie;
document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
}
}

afterEach(() => {
//Intentionally not calling mockServer.resetHandlers here to prevent ServicePulse in flight messages after app unmount to fail.
//mockServer.resetHandlers is being called instead in driver.ts

//Make JSDOM create a fresh document per each test run
jsdom.reconfigure({ url: "http://localhost:3000/" });
localStorage.clear();
sessionStorage.clear();
deleteAllCookies();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All this code was moved into the driver.ts

deleteAllCookies();
afterAll(() => {
console.log("Shutting down mock server");
mockServer.close();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are cleaning up the mock server as per the documentation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am glad that this call to close now works. The await flushpromises now being part of the driver probably does the trick. Nice job 👍

},
test: {
pool: "forks", //https://github.com/vitest-dev/vitest/issues/2008#issuecomment-187106690
pool: "forks",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

forks is now the default.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If forks is now the default, do we still need to explicitly configure it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left it just to be sure

@PhilBastian PhilBastian requested a review from cquirosj October 14, 2025 05:37
@johnsimons
Copy link
Member Author

@cquirosj, if you don't mind, can you review this PR?
There is quite a bit to it, and there is also this PR that explains some of the changes.
The TL;DR is that we divorced the store from the auto refreshes and made the auto refresh based on the component lifecycle (mount/unmount) so that, for example, the refresh stops if all components are unmounted.
We also found an issue with the cleaning-up routines that can cause certain instances to be cleaned up before others.
We have started a discussion in vitest to figure out if the behaviour is a bug or if we are just using it incorrectly, and it seems it is by design, so we ended up moving things around to do with the tests cleanup to ensure things are cleaned up in the correct order.

"lint": "eslint .",
"preview": "vite preview",
"test:component": "vitest ./src",
"test:component": "vitest run ./src",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was able to detect that it was running from CI and not hold the execution in an infinite watch. Making it explicit with run is good 👍

},
test: {
pool: "forks", //https://github.com/vitest-dev/vitest/issues/2008#issuecomment-187106690
pool: "forks",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If forks is now the default, do we still need to explicitly configure it?

deleteAllCookies();
afterAll(() => {
console.log("Shutting down mock server");
mockServer.close();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am glad that this call to close now works. The await flushpromises now being part of the driver probably does the trick. Nice job 👍

Comment on lines +56 to +68
console.log("Starting test");

//run the test
await use(driver);

console.log("Test ended");
//unmount the app after the test runs
driver.disposeApp();

// We need to wait for any pending promises to resolve before resetting handlers and clearing storage
await flushPromises();

console.log("Cleanup after test");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were these console.log statements left here intentionally to be part of the PR? If so, that is ok.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I left them intentionally to provide better diagnostics for the tests

@johnsimons johnsimons merged commit 73e78b3 into master Oct 14, 2025
5 checks passed
@johnsimons johnsimons deleted the john/flaky branch October 14, 2025 22:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants